#include "export.h"

#include "tinyxml.h"

Export::Export(string typeDocFileName)
{
	mParentModuleData = NULL;

	string filePath = string(getenv(ROOT_DIR)).append("Settings\\");
	TiXmlDocument typeDoc( filePath.append(typeDocFileName).c_str() );
	typeDoc.LoadFile();

	TiXmlElement* element = typeDoc.FirstChildElement("Types")->FirstChildElement("Type");

	string type, name;
	while (element)
	{
		TiXmlAttribute* attribute = element->FirstAttribute();
		name = "";
		type = "";
		while (attribute)
		{
			if (string(attribute->Name()) == "name") name = attribute->Value();
			else if (string(attribute->Name()) == "type") type = attribute->Value();
			attribute = attribute->Next();
		}
		if (name != "" && type != "")
		{
			mTypeMap[name] = type;
			//cout << name << ":" << type << endl;
		}
		element = element->NextSiblingElement("Type");
	}

}

void Export::write(string outputName, sc_object* top)
{
	clear();
	mParentModuleData = createModuleDataTree(top);
	createChannelData();
	generateOutput(outputName);
}

void Export::clear()
{
	//TODO: iterate and delete
	mPortInterface.clear();
	mInterfaceList.clear();

	delete mParentModuleData;
	mParentModuleData = NULL;
}

Export::~Export()
{
	clear();
}

void Export::createChannelData()
{
	ChannelData* channelData;
	for (list<ChannelDataInfo*>::iterator i = mPortInterface.begin(); i != mPortInterface.end(); i++)
	{
		for (list<ChannelDataInfo*>::iterator j = mInterfaceList.begin(); j != mInterfaceList.end(); j++)
		{
			if ((*i)->getTargetAddress() == (*j)->getTargetAddress())
			{
				if ((*j)->getInOutType() == TYPE_IN || ((*j)->getInOutType() == TYPE_INOUT && (*i)->getInOutType() == TYPE_OUT))
				{
					channelData = new ChannelData();
					channelData->setSource((*i)->getModuleData(), (*i)->getName(), (*i)->getType());
					channelData->setTarget((*j)->getModuleData(), (*j)->getName(), (*j)->getType());
					
				}
				else if((*j)->getInOutType() == TYPE_OUT || ((*j)->getInOutType() == TYPE_INOUT && (*i)->getInOutType() == TYPE_IN))
				{
					channelData = new ChannelData();
					channelData->setTarget((*i)->getModuleData(), (*i)->getName(), (*i)->getType());
					channelData->setSource((*j)->getModuleData(), (*j)->getName(), (*j)->getType());
				}
				else
				{
					channelData = new ChannelData(true);
					channelData->setSource((*i)->getModuleData(), (*i)->getName(), (*i)->getType());
					channelData->setTarget((*j)->getModuleData(), (*j)->getName(), (*j)->getType());
				}
				(*i)->getModuleData()->addChannelData(channelData);
				break;
			}
		}
	}
}

ModuleData* Export::createModuleDataTree(sc_object* top)
{
	vector<sc_object*> children = top->get_child_objects();

	ModuleData* moduleData = new ModuleData(top->basename());
	ModuleData* childModuleData;

	for (vector<sc_object*>::size_type i = 0; i < children.size(); i++)
	{
		string kind = children[i]->kind();
//cout << children[i]->kind() << " " << children[i]->name() << endl;
		map<string,string>::iterator type = mTypeMap.find(kind);
		if (type != mTypeMap.end())
		{
			if (type->second == "module")
			{
				childModuleData = createModuleDataTree(children[i]);
				moduleData->addModuleData(childModuleData);
			}
			else if (type->second == "method_process")
			{
				moduleData->addProcess(new Process(children[i]->basename(), "method"));
			}
			else if (type->second == "thread_process")
			{
				moduleData->addProcess(new Process(children[i]->basename(), "thread"));
			}
			//else
			{
				
				sc_port_base* portBase = dynamic_cast<sc_port_base*>(children[i]);
				InOutType inOutType;
				if (type->second == "in_port") inOutType = TYPE_IN;
				else if (type->second == "out_port") inOutType = TYPE_OUT;
				else inOutType = TYPE_INOUT;
				if (portBase)
				{
					mPortInterface.push_back(new ChannelDataInfo(moduleData, (int)(portBase->get_interface()), children[i]->basename(), children[i]->kind(), inOutType));
					moduleData->addPortData(new PortData(children[i]->basename(), children[i]->kind(), type->second));
				}
				sc_interface* interf = dynamic_cast<sc_interface*>(children[i]);
				if (interf)
				{ 
					if (dynamic_cast<sc_module*>(children[i]))
					{
						mInterfaceList.push_back(new ChannelDataInfo(childModuleData, (int)interf, children[i]->basename(), children[i]->kind(), inOutType));			
					}
					else
					{
						mInterfaceList.push_back(new ChannelDataInfo(moduleData, (int)interf, children[i]->basename(), children[i]->kind(), inOutType));			
					}
					moduleData->addPortData(new PortData(children[i]->basename(), children[i]->kind(), type->second));
				}

			}
		}
	}

	return moduleData;
}